#include "./chemistry.hpp"
#include "yocto/string/vfs-utils.hpp"
#include "yocto/aqueous/lua.hpp"
#include "yocto/lua/lua-maths.hpp"


Permeability:: Permeability( const SP_Function &sp ) :
factor(1.0),
SP(sp)
{
}

Permeability:: ~Permeability() throw() {}

Chemistry:: ~Chemistry() throw()
{
}

double Chemistry::NoPermeability(double) const throw()
{
    return 0;
}

void Chemistry:: LoadPermeability(species &s, lua_State *)
{
    
    if( lua_isnil(L, -1) )
    {
        std::cerr << "***\t<" << s.name << ">: no permeability" << std::endl;
        s.make<Permeability,SP_Function>(SP0);
        return;
    }
    
    if( lua_isstring(L, -1) )
    {
        size_t                len = 0;
        const char           *ptr = lua_tolstring(L, -1, &len );
        const string          fid(ptr,len); // function name
        Lua::Function<double> lfn(L,fid);   // lua function
        const SP_Function     pfn( lfn );   // as a functor
        s.make<Permeability,SP_Function>(pfn);
        return;
    }
    
    throw exception("<%s>: invalid permeability type!", s.name.c_str());
}

Chemistry:: Chemistry( const string &cfgname ) :
VM( cfgname ),
cfgpath( _vfs::get_file_dir(cfgname) ),
Library( sizeof(Permeability) ),
ChemSys(Library,1e-7),
InitIn(Library),
InitOut(Library),
Solution(2,as_capacity),
SP0( this, & Chemistry::NoPermeability )
{
    
    _lua::species_ctor LdPerm( this, & Chemistry:: LoadPermeability);
    
    //--------------------------------------------------------------------------
    // find chemistry file
    //--------------------------------------------------------------------------
    std::cerr << "-- Loading Chemistry File" << std::endl;
    const string chemfile = Lua::Config::Get<string>( L, "chemistry" );
    const string chemname = cfgpath + chemfile;
    Lua::Config::DoFile(L, chemname);
    
    //--------------------------------------------------------------------------
    // Load the tables
    //--------------------------------------------------------------------------
    std::cerr << "-- Loading Species" << std::endl;
    _lua::load(L, Library, "species", &LdPerm);
    
    std::cerr << "-- Loading Equilibria" << std::endl;
    _lua::load(L,ChemSys,"equilibria");
    
    std::cerr << "-- Loading Initializer@Inside" << std::endl;
    _lua::load(L,InitIn,"initIn");
    
    std::cerr << "-- Loading Initializer@Outside" << std::endl;
    _lua::load(L,InitOut,"initOut");
    
    //--------------------------------------------------------------------------
    // prepare the data
    //--------------------------------------------------------------------------
    std::cerr << "-- Compiling Chemical System" << std::endl;
    ChemSys.build();
    
    std::cerr << "-- Preparing Solutions" << std::endl;
    for( size_t i=1; i <= 2; ++i )
    {
        const solution tmp(Library);
        Solution.push_back(tmp);
    }
    
    InitIn(ChemSys,0.0);
    Solution[1].get(ChemSys.C);
    InitOut(ChemSys,0.0);
    Solution[2].get(ChemSys.C);
    
    std::cerr << "inside = " << ( InitIn.is_variable  ? "variable" : "fixed" ) << "\n" << inside(0.0) << std::endl;
    std::cerr << "outside=" <<  ( InitOut.is_variable ? "variable" : "fixed" ) << "\n" << outside(0.0) << std::endl;
}

void Chemistry:: reload_inside(double t )
{
    std::cerr << "Reload InitIn: " << std::endl;
    std::cerr << InitIn << std::endl;
    InitIn(ChemSys,0.0);
    Solution[1].get(ChemSys.C);
    //std::cerr << "inside = " << ( InitIn.is_variable  ? "variable" : "fixed" ) << "\n" << inside(0.0) << std::endl;
}

solution & Chemistry::inside( double t )  
{ 
    solution &s = Solution[1];
    if( InitIn.is_variable )
    {
        InitIn(ChemSys,t);
        s.get(ChemSys.C);
    }
    return s;
}


solution & Chemistry::outside(double t) 
{
    solution &s = Solution[2];
    if( InitOut.is_variable )
    {
        InitOut(ChemSys,t);
        s.get(ChemSys.C);
    }
    return s;
}


